// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <cantor/parser/ast/ddl.h>
#include <cantor/parser/ast/dql.h>
#include <cantor/parser/ast/stmt.h>

namespace cantor::parser {

    nlohmann::ordered_json DqlNode::to_json_impl() const {
        auto j = DispatchExpr::to_json_impl();
        return j;
    }
    void SelectField::to_stream(std::ostream &os) const {
        os << " ";
        if (expr != nullptr) {
            expr->to_stream(os);
        } else if (wild_card) {
            os<<"*";
        } else {
            KCHECK(false)<<"no expr";
        }
        if (!as_name.empty()) {
            os << " AS " << as_name;
        }
    }

    nlohmann::ordered_json SubqueryExpr::to_json() const {
        nlohmann::ordered_json j = DqlNode::to_json_impl();
        if (query_stmt) {
            j["query_stmt"] = query_stmt->to_json();
        } else {
            j["query_stmt"] = nlohmann::ordered_json();
        }
        return j;
    }


    nlohmann::ordered_json SelectField::to_json() const {
        nlohmann::ordered_json j = DefiniteExpr::to_json_impl();

        if (expr) {
            j["expr"] = expr->to_json();
        } else {
            j["expr"] = nlohmann::ordered_json();
        }
        j["as_name"] =as_name.to_string();
        j["org_name"] = org_name.to_string();
        j["wild_card"] = wild_card;

        return j;
    }

    nlohmann::ordered_json ByItem::to_json() const {
        nlohmann::ordered_json j = DefiniteExpr::to_json_impl();
        j["is_desc"] = is_desc;
        if (expr != nullptr) {
            j["expr"] = expr->to_json();
        } else {
            j["expr"] = nlohmann::ordered_json();
        }
        return j;
    }

    nlohmann::ordered_json WhereClause::to_json() const {
        auto j = DefiniteExpr::to_json_impl();
        if (expr) {
            j["expr"] = expr->to_json();
        } else {
            j["expr"] = nlohmann::ordered_json();
        }
        return j;
    }

    nlohmann::ordered_json HavingClause::to_json() const {
        auto j = DefiniteExpr::to_json_impl();
        if (expr) {
            j["expr"] = expr->to_json();
        } else {
            j["expr"] = nlohmann::ordered_json();
        }
        return j;
    }

    nlohmann::ordered_json OrderByClause::to_json() const {
        nlohmann::ordered_json j = DefiniteExpr::to_json_impl();
        nlohmann::ordered_json its = nlohmann::ordered_json::array();
        for (auto i = 0 ; i < items.size(); ++i) {
            its.push_back(items[i]->to_json());
        }
        j["items"] = its;
        return j;
    }

    nlohmann::ordered_json LimitClause::to_json() const {
        nlohmann::ordered_json j = DefiniteExpr::to_json_impl();
        if (offset) {
            j["offset"] = offset->to_json();
        } else {
            j["offset"] = nlohmann::ordered_json();
        }
        if (count) {
            j["count"] = count->to_json();
        } else {
            j["count"] = nlohmann::ordered_json();
        }
        return j;
    }

    void JoinNode::to_stream(std::ostream &os) const {
        static const char *natural_str[] = {"", " NATURE"};
        static const char *straight_str[] = {"", " STRAIGHT"};
        static const char *join_type_str[] = {"", " inner join", " left join", " right join"};
        os << " (" << left << " )" << natural_str[is_natural] << straight_str[is_straight] << join_type_str[
            join_type] << " (" << right << " )";
        if (expr != nullptr) {
            os << " ON ";
            os << expr;
        }
        if (using_col.size() != 0) {
            os << " USING (";
        }
        for (int i = 0; i < using_col.size(); ++i) {
            os << using_col[i];
            if (i != using_col.size() - 1) {
                os << ", ";
            }
        }
        if (using_col.size() != 0) {
            os << ")";
        }
    }

    nlohmann::ordered_json GroupByClause::to_json() const {
        nlohmann::ordered_json j = DefiniteExpr::to_json_impl();

        auto its = nlohmann::ordered_json::array();
        for (auto i = 0 ; i < items.size(); ++i) {
            its.push_back(items[i]->to_json());
        }
        j["items"] = its;
        return j;
    }

    nlohmann::ordered_json JoinNode::to_json() const {
        nlohmann::ordered_json j = DefiniteExpr::to_json_impl();
        if (left) {
            j["left"] = left->to_json();
        } else {
            j["left"] = nlohmann::ordered_json();
        }
        if (right) {
            j["right"] = right->to_json();
        } else {
            j["right"] = nlohmann::ordered_json();
        }
        j["join_type"] = static_cast<int>(join_type);
        if (expr) {
            j["expr"] = expr->to_json();
        } else {
            j["expr"] = nlohmann::ordered_json();
        }
        auto its = nlohmann::ordered_json::array();
        for (auto i =0; i < using_col.size(); ++i) {
            its.push_back(using_col[i]->to_json());
        }
        j["using_col"] = its;
        j["is_natural"] = is_natural;
        j["is_straight"] = is_straight;
        return j;

    }

    nlohmann::ordered_json ExistsSubqueryExpr::to_json() const {
        nlohmann::ordered_json j = DqlNode::to_json_impl();
        j["is_not"] = is_not;
        if (query_expr) {
            j["query_expr"] = query_expr->to_json();
        } else {
            j["query_expr"] = nlohmann::ordered_json();
        }
        return j;
    }

    nlohmann::ordered_json CompareSubqueryExpr::to_json() const {
        nlohmann::ordered_json j = DqlNode::to_json_impl();
        if (left_expr) {
            j["left_expr"] = left_expr->to_json();
        } else {
            j["left_expr"] = nlohmann::ordered_json();
        }
        if (right_expr) {
            j["right_expr"] = right_expr->to_json();
        } else {
            j["right_expr"] = nlohmann::ordered_json();
        }
        j["cmp_type"] = static_cast<int>(cmp_type);
        j["func_type"] = static_cast<int>(func_type);
        return j;
    }

    nlohmann::ordered_json CommonTableExpr::to_json() const {
        nlohmann::ordered_json j = DqlNode::to_json_impl();
        if (query_expr) {
            j["query_expr"] = query_expr->to_json();
        } else {
            j["query_expr"] = nlohmann::ordered_json();
        }

        auto cols = nlohmann::ordered_json::array();
        for (auto i = 0; i < column_names.size(); ++i) {
            cols.push_back(column_names[i]->to_json());
        }
        j["columns"] = cols;
        j["cte_name"] = name.to_string();
        j["option"] = option.to_string();
        return j;
    }
    nlohmann::ordered_json WithClause::to_json() const {
        nlohmann::ordered_json j = DefiniteExpr::to_json_impl();
        auto cts = nlohmann::ordered_json::array();
        for (auto i = 0 ; i < ctes.size(); ++i) {
            cts.push_back(ctes[i]->to_json());
        }
        j["ctes"] = cts;
        j["option"] = option.to_string();
        return j;
    }

    void TableSource::to_stream(std::ostream &os) const {
        if (derived_table != nullptr) {
            os << " " << derived_table;
        }
        if (table_name != nullptr) {
            os << " " << table_name;
        }
        if (!as_name.empty()) {
            os << " AS " << as_name.value;
        }
        for (int i = 0; i < index_hints.size(); ++i) {
            os << index_hints[i];
        }
        if (partition_names.size() > 0) {
            os << " PARTITION(";
            for (int i = 0; i < partition_names.size(); ++i) {
                os << partition_names[i];
                if (i != partition_names.size() - 1) {
                    os << ",";
                }
            }
            os << ")";
        }
    }

    nlohmann::ordered_json TableSource::to_json() const {
        nlohmann::ordered_json j = DefiniteExpr::to_json_impl();
        if (derived_table != nullptr) {
            j["derived_table"] = derived_table->to_json();
        } else {
            j["derived_table"] = nlohmann::ordered_json();
        }
        if (table_name) {
            j["table_name"] = table_name->to_json();
        } else {
            j["table_name"] = nlohmann::ordered_json();
        }

        j["as_name"] = as_name.to_string();

        auto inds = nlohmann::ordered_json::array();
        for (auto i = 0 ; i < index_hints.size(); ++i) {
            inds.push_back(index_hints[i]->to_json());
        }
        j["index_hints"] = inds;

        auto pnames = nlohmann::ordered_json::array();
        for (auto i = 0 ; i < partition_names.size(); ++i) {
            pnames.push_back(partition_names[i].to_string());
        }
        j["partition_names"] = pnames;
        return j;
    }

    void IndexHint::to_stream(std::ostream &os) const {
        static const char *hint_type_str[] = {"", " USE INDEX", " IGNORE INDEX", " FORCE INDEX"};
        static const char *hint_scope_str[] = {"", " FOR SCAN", " FOR JOIN", " FOR ORDER_BY", " FOR GROUP_BY"};
        os << hint_type_str[hint_type] << hint_scope_str[hint_scope] << " (";
        for (int i = 0; i < index_name_list.size(); ++i) {
            os << " " << index_name_list[i];
            if (i != index_name_list.size() - 1) {
                os << ",";
            }
        }
        os << " )";
    }

    nlohmann::ordered_json IndexHint::to_json() const {
        nlohmann::ordered_json j = DefiniteExpr::to_json_impl();
        j["hint_type"] = hint_type;
        j["hint_scope"] = hint_scope;
        auto inds = nlohmann::ordered_json::array();
        for (auto i = 0; i < index_name_list.size(); ++i) {
            inds.push_back(index_name_list[i].to_string());
        }
        j["index_name_list"] = inds;
        return j;
    }

    bool OrderByClause::is_complex_node() {
        if (is_complex) {
            return true;
        }
        for (int i = 0; i < items.size(); i++) {
            if (items[i]->is_complex_node()) {
                is_complex = true;
                return true;
            }
        }
        return false;
    }

    nlohmann::ordered_json SelectStmtOpts::to_json() const {
        nlohmann::ordered_json j = DefiniteExpr::to_json_impl();
        j["distinct"] = distinct;
        j["sql_cache"] = sql_cache;
        j["calc_found_rows"] = calc_found_rows;
        j["straight_join"] = straight_join;
        j["priority"] = priority;
        return j;
    }

    void SelectStmtOpts::to_stream(std::ostream &os) const {
        static const char *distinct_str[] = {"", " DISTINCT"};
        static const char *sql_cache_str[] = {"", " SQL_CACHE"};
        static const char *calc_found_rows_str[] = {"", " SQL_CALC_FOUND_ROWS"};
        static const char *straight_join_str[] = {"", " STRAIGHT_JOIN"};
        os << distinct_str[distinct] << straight_join_str[straight_join] << sql_cache_str[sql_cache] <<
                calc_found_rows_str[calc_found_rows];
        os << priority_str[priority];
    }

    nlohmann::ordered_json IndirectionIndex::to_json() const {
        nlohmann::ordered_json j = Indirection::to_json_impl();
        if (index_expr) {
            j["index_expr"] = index_expr->to_json();
        } else {
            j["index_expr"] = nlohmann::ordered_json();
        }
        return j;
    }

    void IndirectionIndex::to_stream(std::ostream &os) const {
        os<<"[";
        if (index_expr) {
            index_expr->to_stream(os);
        }
        os<<"]";
    }

    nlohmann::ordered_json IndirectionSlice::to_json() const {
        nlohmann::ordered_json j = Indirection::to_json_impl();
        if (slice_low) {
            j["slice_low"] = slice_low->to_json();
        } else {
            j["slice_low"] = nlohmann::ordered_json();
        }
        if (slice_high) {
            j["slice_high"] = slice_high->to_json();
        } else {
            j["slice_high"] = nlohmann::ordered_json();
        }
        if (slice_step) {
            j["slice_step"] = slice_step->to_json();
        } else {
            j["slice_step"] = nlohmann::ordered_json();
        }
        return j;
    }

    void IndirectionSlice::to_stream(std::ostream &os) const {
        os<<"[";
        if (slice_low) {
            slice_low->to_stream(os);
        }
        os<<":";
        if (slice_high) {
            slice_high->to_stream(os);
        }

        if (slice_step) {
            os<<":";
            slice_step->to_stream(os);
        }
        os<<"]";
    }
}  // namespace cantor::parser
